﻿using System.Collections.Generic;
using System.Net.Sockets;

namespace AhpilyServer
{
    /// <summary>
    /// 封装客户端的连接对象
    /// </summary>
    public class ClientPeer
    {
        /// <summary>
        /// 连接对象
        /// </summary>
        public Socket ClientSocket { get; set; }

        public ClientPeer()
        {
            ReceiveArgs = new SocketAsyncEventArgs
            {
                UserToken = this
            };
            ReceiveArgs.SetBuffer(new byte[1024],0,1024);
            mSendArgs = new SocketAsyncEventArgs();
            mSendArgs.Completed += SendEventArg_Completed;
        }

        #region 接收数据
        /// <summary>
        /// 接收的异步Socket请求
        /// </summary>
        public SocketAsyncEventArgs ReceiveArgs { get; set; }

        public delegate void ClientReveiveCompleted(ClientPeer client, SocketMessage value);
        public ClientReveiveCompleted OnReveiveMessageCompleted;

        /// <summary>
        /// 一旦接收到数据 就存到缓存区
        /// </summary>
        private List<byte> mDataCache = new List<byte>();
        /// <summary>
        /// 是否正在处理接收的数据
        /// </summary>
        private bool isReceiveProcess = false;

        /// <summary>
        /// 自身处理数据包
        /// </summary>
        public void StartReceive(byte[] packet)
        {
            mDataCache.AddRange(packet);
            if (!isReceiveProcess)
            {
                ProcessReceive();
            }
        }

        /// <summary>
        /// 处理接收的数据
        /// </summary>
        private void ProcessReceive()
        {
            isReceiveProcess = true;

            //解析数据包
            var data = EncodeTool.DecodePacket(ref mDataCache);

            if (data == null)
            {
                isReceiveProcess = false;
                return;
            }

            //将数据转成一个具体的对象
            SocketMessage message = EncodeTool.DecodeMessage(data);

            //回调给上层
            OnReveiveMessageCompleted?.Invoke(this, message);

            //尾递归
            ProcessReceive();
        }
        #endregion

        #region 发送数据
        /// <summary>
        /// 发送时 连接异常终端的处理回调
        /// </summary>
        public delegate void SendDisconnect(ClientPeer client, string reason);
        public SendDisconnect OnSendDisconnect;
        /// <summary>
        /// 待发送消息的队列
        /// </summary>
        private Queue<byte[]> mSendQueue = new Queue<byte[]>();
        /// <summary>
        /// 发送的异步套接字操作
        /// </summary>
        private SocketAsyncEventArgs mSendArgs;
        private bool isSendProcess = false;

        /// <summary>
        /// 发送网络消息
        /// </summary>
        /// <param name="opCode">操作码</param>
        /// <param name="subCode">子操作</param>
        /// <param name="value">参数</param>
        public void SendMessage(int opCode, int subCode, object value)
        {
            SocketMessage message = new SocketMessage(opCode, subCode, value);
            byte[] data = EncodeTool.EncodeMessage(message);
            byte[] packet = EncodeTool.EncodePacket(data);

            //存入消息队列
            mSendQueue.Enqueue(packet);
            //开始发送
            if (!isSendProcess)
            {
                Send();
            }
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        private void Send()
        {
            isSendProcess = true;

            //如果发送队列为空 暂停发送
            if (mSendQueue.Count == 0)
            {
                isSendProcess = false;
                return;
            }
            //取出一条数据
            byte[] packet = mSendQueue.Dequeue();
            //设置消息 发送的异步套接字操作 的发送数据缓冲区
            mSendArgs.SetBuffer(packet, 0, packet.Length);
            bool result = ClientSocket.SendAsync(mSendArgs);
            if (result == false)
            {
                ProcessSend();
            }
        }
        private void SendEventArg_Completed(object sender, SocketAsyncEventArgs args)
        {
            ProcessSend();
        }
        /// <summary>
        /// 当异步发送请求完成的时候调用
        /// </summary>
        private void ProcessSend()
        {
            //发送结果
            if (mSendArgs.SocketError == SocketError.Success)
            {
                Send();
            }
            else
            {
                //发送出错了
                OnSendDisconnect(this, mSendArgs.SocketError.ToString());
            }
        }
        #endregion

        #region 断开连接
        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            //清空数据
            isSendProcess = false;
            isReceiveProcess = false;
            mSendQueue.Clear();
            mDataCache.Clear();

            ClientSocket.Shutdown(SocketShutdown.Both);
            ClientSocket.Close();
            ClientSocket = null;
        }
        #endregion
    }
}
